/*
 * Copyright 2011 www.pretty-tools.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#pragma once
#include "resource.h"       // main symbols

#include "WebSlideShowExtension_i.h"
#include "../XPCOMAdapter/XPCOMAdapter_i.h"

#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
#endif

#define CONFIG_PROPERTY_bool(name) \
	__declspec(property(get = Get##name, put = Put##name)) bool name;\
	bool Get##name() { VARIANT_BOOL name = VARIANT_FALSE; if (FAILED(get_##name(&name))) return false; return VARIANT_TRUE == name; }\
	bool Put##name(bool name) { return SUCCEEDED(put_##name(name ? VARIANT_TRUE : VARIANT_FALSE)); }

#define CONFIG_PROPERTY_READONLY_STRING(name) \
	__declspec(property(get = Get##name, put = Put##name)) CString name;\
	CString Get##name() { CComBSTR name; if (FAILED(get_##name(&name))) return CString(); return (CString)name; }\

#define CONFIG_PROPERTY_STRING(name) \
	__declspec(property(get = Get##name, put = Put##name)) CString name;\
	CString Get##name() { CComBSTR name; if (FAILED(get_##name(&name))) return CString(); return (CString)name; }\
	bool Put##name(LPCSTR pszName) { return SUCCEEDED(put_##name(CComBSTR(pszName))); }\
	bool Put##name(LPCWSTR pszName) { return SUCCEEDED(put_##name(CComBSTR(pszName))); }

#define CONFIG_PROPERTY_long(name) \
	__declspec(property(get = Get##name, put = Put##name)) long name;\
	long Get##name() { long name = 0; if (FAILED(get_##name(&name))) return 0; return name; }\
	bool Put##name(long name) { return SUCCEEDED(put_##name(name)); }

#define CONFIG_PROPERTY_LONGLONG(name) \
	__declspec(property(get = Get##name, put = Put##name)) LONGLONG name;\
	LONGLONG Get##name() { LONGLONG name = 0; if (FAILED(get_##name(&name))) return 0; return name; }\
	bool Put##name(LONGLONG name) { return SUCCEEDED(put_##name(name)); }

// CScriptConfiguration

class ATL_NO_VTABLE CScriptConfiguration 
    : public CComObjectRootEx<CComSingleThreadModel>
	, public CComCoClass<CScriptConfiguration, &CLSID_ScriptConfiguration>
	, public IDispatchImpl<IConfiguration, &IID_IConfiguration, &LIBID_WebSlideShowExtensionLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
	, public IObjectSafetyImpl<CScriptConfiguration, INTERFACESAFE_FOR_UNTRUSTED_CALLER>
{
public:
	CScriptConfiguration()
        : preferences( _PROJECT_REGKEY )
        , resourceBundle(L"res://{8706081B-A7C0-4EC6-BDFF-A6F54B24A837}/CONFIGURATION.PROPERTIES")
	{
        ATLTRACE( _T("CScriptConfiguration::CScriptConfiguration()\n") );
	}

    ~CScriptConfiguration()
	{
        ATLTRACE( _T("CScriptConfiguration::~CScriptConfiguration()\n") );
	}

DECLARE_REGISTRY_RESOURCEID(IDR_SCRIPTCONFIGURATION)

BEGIN_CATEGORY_MAP(CScriptConfiguration)
	IMPLEMENTED_CATEGORY(CATID_SafeForScripting)
	IMPLEMENTED_CATEGORY(CATID_SafeForInitializing)
END_CATEGORY_MAP()

BEGIN_COM_MAP(CScriptConfiguration)
	COM_INTERFACE_ENTRY(IDispatch)
	COM_INTERFACE_ENTRY(IConfiguration)
	COM_INTERFACE_ENTRY(IObjectSafety)
END_COM_MAP()

	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{
	}

private:
    class PrefBranch : public CComPtr<IPrefBranch>
    {
    public:
        PrefBranch(const CComBSTR& aPrefRoot)
        {
            if (SUCCEEDED(CoCreateInstance(L"XPCOMAdapter.PrefBranch.1")))
                p->InitWithKey(aPrefRoot);
        }

        STDMETHOD(getBool)(const CComBSTR &aPrefName, bool bDefault, VARIANT_BOOL* pRet)
        {
            RETURN_FAILED(Validate(aPrefName, pRet))
            
            VARIANT_BOOL vbHasValue = VARIANT_FALSE;
            RETURN_FAILED(p->prefHasUserValue(aPrefName, &vbHasValue))
            if (VARIANT_TRUE == vbHasValue)
                return p->getBoolPref(aPrefName, pRet);
            else
            {
                *pRet = ToVariant(bDefault);

                return S_OK;
            }
        }

        STDMETHOD(setBool)(const CComBSTR &aPrefName, VARIANT_BOOL aValue)
        {
            RETURN_FAILED(Validate(aPrefName))

            return p->setBoolPref(aPrefName, aValue);
        }

        STDMETHOD(getString)(const CComBSTR &aPrefName, CComBSTR bsDefault, BSTR* pRet)
        {
            RETURN_FAILED(Validate(aPrefName, pRet))
            
            VARIANT_BOOL vbHasValue = VARIANT_FALSE;
            RETURN_FAILED(p->prefHasUserValue(aPrefName, &vbHasValue))
            if (VARIANT_TRUE == vbHasValue)
                return p->getCharPref(aPrefName, pRet);
            else
            {
                *pRet = bsDefault.Detach();

                return S_OK;
            }
        }

        STDMETHOD(setString)(const CComBSTR &aPrefName, BSTR aValue)
        {
            RETURN_FAILED(Validate(aPrefName))
            if (!aValue)
                return E_INVALIDARG;

            return p->setCharPref(aPrefName, aValue);
        }

        STDMETHOD(getInt)(const CComBSTR &aPrefName, LONG nDefault, LONG* pRet)
        {
            RETURN_FAILED(Validate(aPrefName, pRet))
            
            VARIANT_BOOL vbHasValue = VARIANT_FALSE;
            RETURN_FAILED(p->prefHasUserValue(aPrefName, &vbHasValue))
            if (VARIANT_TRUE == vbHasValue)
                return p->getIntPref(aPrefName, pRet);
            else
            {
                *pRet = nDefault;

                return S_OK;
            }
        }

        STDMETHOD(setInt)(const CComBSTR &aPrefName, LONG aValue)
        {
            RETURN_FAILED(Validate(aPrefName))

            return p->setIntPref(aPrefName, aValue);
        }

        STDMETHOD(getInt64)(const CComBSTR &aPrefName, LONGLONG nDefault, LONGLONG* pRet)
        {
            RETURN_FAILED(Validate(aPrefName, pRet))
            
            VARIANT_BOOL vbHasValue = VARIANT_FALSE;
            RETURN_FAILED(p->prefHasUserValue(aPrefName, &vbHasValue))
            if (VARIANT_TRUE == vbHasValue)
                return p->getInt64Pref(aPrefName, pRet);
            else
            {
                *pRet = nDefault;

                return S_OK;
            }
        }

        STDMETHOD(setInt64)(const CComBSTR &aPrefName, LONGLONG aValue)
        {
            RETURN_FAILED(Validate(aPrefName))

            return p->setInt64Pref(aPrefName, aValue);
        }

    private:
        HRESULT Validate(const CComBSTR &aPrefName)
        {
            if (!p)
                return E_UNEXPECTED;
            if (!aPrefName)
                return E_INVALIDARG;

            return S_OK;
        }

        HRESULT Validate(const CComBSTR &aPrefName, LPVOID pRet)
        {
            if (!p)
                return E_UNEXPECTED;
            if (!aPrefName)
                return E_INVALIDARG;
            if (!pRet)
                return E_POINTER;

            return S_OK;
        }
    };

    class ResourceBundle : public CComPtr<IStringBundle>
    {
    public:
        ResourceBundle(const CComBSTR &bsURL)
        {
            if (SUCCEEDED(CoCreateInstance(L"XPCOMAdapter.StringBundle.1")))
                p->Init(bsURL);
        }

        HRESULT getString(const CComBSTR &name, BSTR* pRet)
        {
            RETURN_FAILED(Validate(name, pRet))

            return p->GetStringFromName(name, pRet);
        }

        HRESULT getInt(const CComBSTR &name, LONG* pRet)
        {
            RETURN_FAILED(Validate(name, pRet))

            CComBSTR bsValue;
            RETURN_FAILED(p->GetStringFromName(name, &bsValue))

            *pRet = 0;
            
            return (1 == swscanf(bsValue, L"%d", pRet)) ? S_OK : E_FAIL;
        }

        LONG getIntValue(const CComBSTR &name)
        {
            LONG lValue = 0;
            getInt(name, &lValue);
            return lValue;
        }

    private:
        HRESULT Validate(const CComBSTR &aPrefName, LPVOID pRet)
        {
            if (!p)
                return E_UNEXPECTED;
            if (!aPrefName)
                return E_INVALIDARG;
            if (!pRet)
                return E_POINTER;

            return S_OK;
        }
    };

private:
    PrefBranch     preferences;
    ResourceBundle resourceBundle;

public:
    STDMETHOD(get_domainName)(BSTR* pVal) { return this->resourceBundle.getString(L"domain.name", pVal); }
    STDMETHOD(get_urlHomePage)(BSTR* pVal) { return this->resourceBundle.getString(L"home.page.url", pVal); }
    STDMETHOD(get_urlHelpSettingsPage)(BSTR* pVal) { return this->resourceBundle.getString(L"help.settings.page.url", pVal); }
    STDMETHOD(get_urlHelpRulesPage)(BSTR* pVal) { return this->resourceBundle.getString(L"help.rules.page.url", pVal); }
    STDMETHOD(get_urlPredefinedRules)(BSTR* pVal) { return this->resourceBundle.getString(L"predefined.rules.url", pVal); }
    CONFIG_PROPERTY_READONLY_STRING(urlPredefinedRules)
    STDMETHOD(get_urlSubscriptions)(BSTR* pVal) { return this->resourceBundle.getString(L"subscriptions.url", pVal); }
    CONFIG_PROPERTY_READONLY_STRING(urlSubscriptions)
    // slide show delay
    STDMETHOD(get_slideShowDelay)(LONG* pVal) { return this->preferences.getInt(L"slideShowDelay", 3, pVal); }
    STDMETHOD(put_slideShowDelay)(LONG slideShowDelay) { return this->preferences.setInt(L"slideShowDelay", slideShowDelay); }
    // slide auto-start
    STDMETHOD(get_slideAutoStart)(VARIANT_BOOL* pVal) { return this->preferences.getBool(L"slideAutoStart", false, pVal); }
    STDMETHOD(put_slideAutoStart)(VARIANT_BOOL slideAutoStart) { return this->preferences.setBool(L"slideAutoStart", slideAutoStart); }
    CONFIG_PROPERTY_bool(slideAutoStart)
    // showPanelOnlyWhenMouseOverIt
    STDMETHOD(get_showPanelOnlyWhenMouseOverIt)(VARIANT_BOOL* pVal) { return this->preferences.getBool(L"showPanelOnlyWhenMouseOverIt", false, pVal); }
    STDMETHOD(put_showPanelOnlyWhenMouseOverIt)(VARIANT_BOOL showPanelOnlyWhenMouseOverIt) { return this->preferences.setBool(L"showPanelOnlyWhenMouseOverIt", showPanelOnlyWhenMouseOverIt); }
    CONFIG_PROPERTY_bool(showPanelOnlyWhenMouseOverIt)
    // shrink images to fit screen
    STDMETHOD(get_shrinkToFitScreen)(VARIANT_BOOL* pVal) { return this->preferences.getBool(L"shrinkToFitScreen", false, pVal); }
    STDMETHOD(put_shrinkToFitScreen)(VARIANT_BOOL shrinkToFitScreen) { return this->preferences.setBool(L"shrinkToFitScreen", shrinkToFitScreen); }
    CONFIG_PROPERTY_bool(shrinkToFitScreen)
    // zoom images to fit screen
    STDMETHOD(get_zoomToFitScreen)(VARIANT_BOOL* pVal) { return this->preferences.getBool(L"zoomToFitScreen", false, pVal); }
    STDMETHOD(put_zoomToFitScreen)(VARIANT_BOOL zoomToFitScreen) { return this->preferences.setBool(L"zoomToFitScreen", zoomToFitScreen); }
    CONFIG_PROPERTY_bool(zoomToFitScreen)
    // repeat
    STDMETHOD(get_repeat)(VARIANT_BOOL* pVal) { return this->preferences.getBool(L"repeat", false, pVal); }
    STDMETHOD(put_repeat)(VARIANT_BOOL repeat) { return this->preferences.setBool(L"repeat", repeat); }
    CONFIG_PROPERTY_bool(repeat)
    // scroll step
    STDMETHOD(get_imageScrollStep)(LONG* pVal) { return this->preferences.getInt(L"imageScrollStep", 50, pVal); }
    STDMETHOD(put_imageScrollStep)(LONG imageScrollStep) { return this->preferences.setInt(L"imageScrollStep", imageScrollStep); }
    // Zoom step
    STDMETHOD(get_imageZoomStep)(LONG* pVal) { return this->preferences.getInt(L"imageZoomStep", 20, pVal); }
    STDMETHOD(put_imageZoomStep)(LONG imageZoomStep) { return this->preferences.setInt(L"imageZoomStep", imageZoomStep); }
    // min image size
    STDMETHOD(get_minImageWidth)(LONG* pVal) { return this->preferences.getInt(L"minImageWidth", 300, pVal); }
    STDMETHOD(put_minImageWidth)(LONG minImageWidth) { return this->preferences.setInt(L"minImageWidth", minImageWidth); }
    STDMETHOD(get_minImageHeight)(LONG* pVal) { return this->preferences.getInt(L"minImageHeight", 300, pVal); }
    STDMETHOD(put_minImageHeight)(LONG minImageHeight) { return this->preferences.setInt(L"minImageHeight", minImageHeight); }
    // open image link to full screen
    STDMETHOD(get_fullscreenImageLink)(VARIANT_BOOL* pVal) { return this->preferences.getBool(L"fullscreenImageLink", true, pVal); }
    STDMETHOD(put_fullscreenImageLink)(VARIANT_BOOL fullscreenImageLink) { return this->preferences.setBool(L"fullscreenImageLink", fullscreenImageLink); }
    CONFIG_PROPERTY_bool(fullscreenImageLink)
    // check for predefined rules
    STDMETHOD(get_useSubscriptions)(VARIANT_BOOL* pVal) { return this->preferences.getBool(L"useSubscriptions", true, pVal); }
    STDMETHOD(put_useSubscriptions)(VARIANT_BOOL useSubscriptions) { return this->preferences.setBool(L"useSubscriptions", useSubscriptions); }
    CONFIG_PROPERTY_bool(useSubscriptions)
    // check for predefined rules
    STDMETHOD(get_updatePredefinedRules)(VARIANT_BOOL* pVal) { return this->preferences.getBool(L"updatePredefinedRules", true, pVal); }
    STDMETHOD(put_updatePredefinedRules)(VARIANT_BOOL updatePredefinedRules) { return this->preferences.setBool(L"updatePredefinedRules", updatePredefinedRules); }
    CONFIG_PROPERTY_bool(updatePredefinedRules)
    // update interval in minutes
    STDMETHOD(get_updateInterval)(LONG* pVal) { return this->preferences.getInt(L"updateInterval", this->resourceBundle.getIntValue(L"default.update.interval"), pVal); }
    STDMETHOD(put_updateInterval)(LONG updateInterval) { return this->preferences.setInt(L"updateInterval", updateInterval); }
    CONFIG_PROPERTY_long(updateInterval)
    // last timestamp
    STDMETHOD(get_updateTimeStamp)(BSTR* pVal) { return this->preferences.getString(L"updateTimeStamp", L"", pVal); }
    STDMETHOD(put_updateTimeStamp)(BSTR updateTimeStamp) { return this->preferences.setString(L"updateTimeStamp", updateTimeStamp); }
    CONFIG_PROPERTY_STRING(updateTimeStamp)
    // Last-Modified time for predefined rules
    STDMETHOD(get_predefinedRulesLastModified)(BSTR* pVal) { return this->preferences.getString(L"predefinedRulesLastModified", L"", pVal); }
    STDMETHOD(put_predefinedRulesLastModified)(BSTR predefinedRulesLastModified) { return this->preferences.setString(L"predefinedRulesLastModified", predefinedRulesLastModified); }
    CONFIG_PROPERTY_STRING(predefinedRulesLastModified)
    // time to wait before update in ms
    STDMETHOD(get_waitBeforeUpdate)(LONG* pVal) { return this->resourceBundle.getInt(L"wait.before.update", pVal); }
	// Update
    STDMETHOD(get_CheckForUpdateInterval)(LONG* pVal) { return this->preferences.getInt(L"CheckForUpdateInterval", 3, pVal); }
    STDMETHOD(put_CheckForUpdateInterval)(LONG updateInterval) { return this->preferences.setInt(L"CheckForUpdateInterval", updateInterval); }
    CONFIG_PROPERTY_long(CheckForUpdateInterval)
    STDMETHOD(get_LastCheckForUpdateTime)(LONGLONG* pVal) { return this->preferences.getInt64(L"LastCheckForUpdateTime", 0, pVal); }
    STDMETHOD(put_LastCheckForUpdateTime)(LONGLONG updateTime) { return this->preferences.setInt64(L"LastCheckForUpdateTime", updateTime); }
	CONFIG_PROPERTY_LONGLONG(LastCheckForUpdateTime);
};

typedef CComObject<CScriptConfiguration> Configuration;

OBJECT_ENTRY_AUTO(__uuidof(ScriptConfiguration), CScriptConfiguration)
